/*
Copyright (c) 2008-2009, Oleg Estekhin
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.
    * Neither the name of the copyright holders nor the names of its
      contributors may be used to endorse or promote products derived from this
      software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package fractal.tile;

import fractal.math.fractal.Fractal;

/** A tile represents a rectangular region of an image. */
public final class Tile {

    private final long x;

    private final long y;

    private final double scale;

    private final int width;

    private final int height;

    private final Fractal fractal;


    /**
     * Creates a new {@code Tile} object.
     *
     * @param x the X coordinate of the top-left corner of the tile premultiplied with {@code scale}
     * @param y the Y coordinate of the top-left corner of the tile premultiplied with {@code scale}
     * @param scale the scale factor
     * @param width the tile width in pixels
     * @param height the tile height in pixels
     * @param fractal the fractal
     *
     * @throws IllegalArgumentException if {@code scale} is infinite, not a number or less then or equal to zero
     * @throws IllegalArgumentException if {@code width} or {@code height} is negative
     * @throws NullPointerException if {@code fractal} is {@code null}
     */
    public Tile(long x, long y, double scale, int width, int height, Fractal fractal) {
        if (Double.isInfinite(scale) || Double.isNaN(scale)) {
            throw new IllegalArgumentException("scale is infinite or NaN");
        }
        if (scale <= 0.0) {
            throw new IllegalArgumentException("scale is zero or negative");
        }
        if (width < 0) {
            throw new IllegalArgumentException("width is negative");
        }
        if (height < 0) {
            throw new IllegalArgumentException("height is negative");
        }
        if (fractal == null) {
            throw new NullPointerException("fractal is null");
        }
        this.x = x;
        this.y = y;
        this.scale = scale;
        this.width = width;
        this.height = height;
        this.fractal = fractal;
    }


    /**
     * Returns the prescaled X coordinate of the top-left corner of the tile.
     *
     * @return the prescaled X coordinate
     */
    public long getX() {
        return x;
    }

    /**
     * Returns the prescaled Y coordinate of the top-left corner of the tile.
     *
     * @return the prescaled Y coordinate
     */
    public long getY() {
        return y;
    }

    /**
     * Returns the scale factor of the tile.
     *
     * @return the scale factor
     */
    public double getScale() {
        return scale;
    }

    /**
     * Returns the tile width.
     *
     * @return the width
     */
    public int getWidth() {
        return width;
    }

    /**
     * Returns the tile height.
     *
     * @return the height
     */
    public int getHeight() {
        return height;
    }

    /**
     * Returns the tile fractal.
     *
     * @return the fractal
     */
    public Fractal getFractal() {
        return fractal;
    }


    @Override
    public int hashCode() {
        int result = 17;
        result = 37 * result + (int) (x ^ x >>> 32);
        result = 37 * result + (int) (y ^ y >>> 32);
        long longScale = Double.doubleToRawLongBits(scale);
        result = 37 * result + (int) (longScale ^ longScale >>> 32);
        result = 37 * result + width;
        result = 37 * result + height;
        result = 37 * result + fractal.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj instanceof Tile) {
            Tile tile = (Tile) obj;
            return x == tile.x
                    && y == tile.y
                    && scale == tile.scale
                    && width == tile.width
                    && height == tile.height
                    && fractal.equals(tile.fractal);
        } else {
            return false;
        }
    }

    @Override
    public String toString() {
        StringBuilder buffer = new StringBuilder();
        buffer.append("Tile[")
                .append("x=").append(x)
                .append(',')
                .append("y=").append(y)
                .append(',')
                .append("scale=").append(scale)
                .append(',')
                .append("width=").append(width)
                .append(',')
                .append("height=").append(height)
                .append(',')
                .append("fractal=").append(fractal)
                .append(']');
        return buffer.toString();
    }

}
